package com.seguriapp.dao;

import static org.hibernate.criterion.Restrictions.between;
import static org.hibernate.criterion.Restrictions.eq;
import static org.hibernate.criterion.Restrictions.ge;
import static org.hibernate.criterion.Restrictions.ilike;
import static org.hibernate.criterion.Restrictions.le;
import static org.hibernate.criterion.Restrictions.or;

import java.util.Calendar;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import com.seguriapp.base.GenericDaoImpl;
import com.seguriapp.entities.Alert;
import com.seguriapp.util.SearchFilterEntity;

/**
 * Data access object for {@link Alert} entities using Hibernate.
 * 
 * @author Tania Gonzales (taniadgv@gmail.com)
 */
@Repository
public class AlertDaoImpl extends GenericDaoImpl<Alert> implements AlertDao {

  private static final String DATE_FIELD = "date";
  private static final String NAMES_FIELD = "names";
  private static final String LASTNAMES_FIELD = "lastNames";

  public AlertDaoImpl() {
    super(Alert.class);
  }

  @SuppressWarnings("unchecked")
  public List<Alert> listAlertsOrderByDate() {
    return getSession().createCriteria(Alert.class).addOrder(Order.desc(DATE_FIELD)).list();
  }

  @SuppressWarnings("unchecked")
  public List<Alert> listAlertsOrderByColumn(final SearchFilterEntity searchFilter) {
    final Criteria criteria = setCriteriaForQuery(searchFilter);
    if (searchFilter.getColumnSort() != null) {
      if (searchFilter.isAscending()) {
        criteria.addOrder(Order.asc(searchFilter.getColumnSort()));
      } else {
        criteria.addOrder(Order.desc(searchFilter.getColumnSort()));
      }
    } else {
      criteria.addOrder(Order.desc(DATE_FIELD));
    }
    if (searchFilter.isAllResults()) {
      return criteria.list();
    } else {
      return criteria.setMaxResults(searchFilter.getPageSize()).setFirstResult((searchFilter.getPage() - 1) * searchFilter.getPageSize()).list();
    }
  }

  public long getAlertsTotalPages(final SearchFilterEntity searchFilter) {
    final Criteria criteria = setCriteriaForQuery(searchFilter);
    criteria.setProjection(Projections.rowCount());
    final long total = Long.valueOf(criteria.uniqueResult().toString());
    long pages = 0;
    if (searchFilter.getPageSize() != 0) {
      pages = total / searchFilter.getPageSize();
      if (total % searchFilter.getPageSize() > 0) {
        pages++;
      }
    }
    return pages;
  }

  private Criteria setCriteriaForQuery(final SearchFilterEntity searchFilter) {
    final Criteria criteria = getSession().createCriteria(Alert.class);
    // by start date
    if (searchFilter.getStartDate() != null) {
      criteria.add(ge(DATE_FIELD, searchFilter.getStartDate()));
    }
    // by end date
    if (searchFilter.getEndDate() != null) {
      Calendar cal = Calendar.getInstance();
      cal.setTime(searchFilter.getEndDate());
      cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY));
      cal.set(Calendar.MINUTE, cal.getMaximum(Calendar.MINUTE));
      cal.set(Calendar.SECOND, cal.getMaximum(Calendar.SECOND));
      criteria.add(le(DATE_FIELD, cal.getTime()));
    }
    // by police station
    if (searchFilter.isSearchByPolStation() && searchFilter.getPoliceStationId() != 0) {
      criteria.add(eq("entity.id", searchFilter.getPoliceStationId()));
    } else {
      // by latitude and longitude
      if (searchFilter.getLatitude() != 0 && searchFilter.getLongitude() != 0 && searchFilter.getDistance() != 0) {
        criteria.add(
            between("latitude", searchFilter.getLatitude() - searchFilter.getDistance(), searchFilter.getLatitude() + searchFilter.getDistance()))
            .add(
                between("longitude", searchFilter.getLongitude() - searchFilter.getDistance(),
                    searchFilter.getLongitude() + searchFilter.getDistance()));
      }
    }
    // by names and lastnames
    if (searchFilter.getNames() != null && !searchFilter.getNames().isEmpty()) {
      criteria.add(or(ilike(NAMES_FIELD, searchFilter.getNames(), MatchMode.ANYWHERE),
          ilike(LASTNAMES_FIELD, searchFilter.getNames(), MatchMode.ANYWHERE)));
    }
    // by user entities
    if (searchFilter.getIdsList() != null && !searchFilter.getIdsList().isEmpty()) {
      final Disjunction disjunction = Restrictions.disjunction();
      for (final Long entityId : searchFilter.getIdsList()) {
        disjunction.add(eq("entity.id", entityId));
      }
      criteria.add(disjunction);
    }
    return criteria;
  }

}
